/*
 * Reflection v0.1
 * Written by Ara Pehlivanian (http://arapehlivanian.com)
 * 
 * This work is licensed under a Creative Commons Licence
 * http://creativecommons.org/licenses/by-nd/2.5/
 */
 
ARA = window.ARA || {};
ARA.effects = ARA.effects || {};

/**
 * Creates a reflection object
 */
ARA.effects.Reflection = function(params){
	this.supportsCanvas = ARA.effects.supportsCanvas();
	if(!this.supportsCanvas) return;
	
	params = params || {};
	
	this.constants = {};
	this.constants.wrapperID = "_wrapper";
	this.constants.wrapperClassName = "_wrapper";
	this.constants.canvasID = "_reflection";
	this.constants.canvasClassName = "_reflection";
	
	// Default values
	this.wrapImages = true;
	this.alpha = .40;
	this.depth = null; // unknown at object creation time (defaults to half the image height [see createReflection function])
	this.imageAsBlock = true;
};

/**
 * Returns the object reflection type
 */
ARA.effects.Reflection.prototype.toString = function(){
	return "Reflection";
};

/**
 * Runs through the images collection and creates a reflection
 * for each of them
 */
ARA.effects.Reflection.prototype.reflect = function(){
	if(!this.supportsCanvas) return;

	for(var i=0; i<this.images.length; i++){
		var image = this.images[i];
		if(image.wrapImage) this.wrapImage(image.element);
		this.createReflection(image);
	}
};

/**
 * Creates a wrapper div around an image in the DOM and returns
 * a reference to it.
 * @param  {Ref}  el  A DOM reference to an image
 * @param  {Ref}
 */
ARA.effects.Reflection.prototype.wrapImage = function(el){
	var cName = this.constants.wrapperClassName;
	var wrapper = document.createElement("div");
		wrapper.id = el.id + this.constants.wrapperID;
		wrapper.className = (wrapper.className.length > 0) ? wrapper.className + cName : cName.substring(1, cName.length);
	el.parentNode.insertBefore(wrapper, el.nextSibling);
	wrapper.appendChild(el);
	return wrapper;	
};

/**
 * An internal array where image references are stored
 */
ARA.effects.Reflection.prototype.images = [];


/**
 * Adds an image to the images collection
 * @param  {Ref/ID}  image   Either a DOM reference or and ID to an image in the DOM
 * @param  {Object}  params  A JSON object of parameters:
 *                             {Boolean}  wrapImage     Optional. Whether or not to wrap an image and its reflection with a div
 *                             {Float}    alpha         Optional. Default: .25. Values from 0 to 1. The opacity to apply to the reflection
 *                             {Int}      depth         Optional. Default: half img height. How deep the reflection goes.
 *                             {Int}      imageAsBlock  Optional. Default: true. Whether or not to leave the image as display: inline or to set it as block (so that the reflection goes underneath it)
 */
ARA.effects.Reflection.prototype.addImage = function(image, params){	
	if(!this.supportsCanvas) return;
	
	image = ARA.dom.getElement(image);
	params = params || {};
	
	this.images.push({
		"element":image,
		"wrapImage":params.wrapImage || this.wrapImages,
		"alpha":params.alpha || this.alpha,
		"depth":params.depth || this.depth,
		"imageAsBlock":params.imageAsBlock || this.imageAsBlock
	});
};

/**
 * Collects all images with a given class name and adds them to the images 
 * collection
 * @param  {String}  className  Class name to grab
 * @param  {Object}  params  A JSON object of parameters:
 *                             {Ref/ID}   startPoint    Optional. Default: document. What node to start collecting from
 *                             {Boolean}  wrapImage     Optional. Whether or not to wrap an image and its reflection with a div
 *                             {Float}    alpha         Optional. Default: .25. Values from 0 to 1. The opacity to apply to the reflection
 *                             {Int}      depth         Optional. Default: half img height. How deep the reflection goes.
 *                             {Int}      imageAsBlock  Optional. Default: true. Whether or not to leave the image as display: inline or to set it as block (so that the reflection goes underneath it)
 */
ARA.effects.Reflection.prototype.addImagesByClassName = function(className, params){
	if(!this.supportsCanvas) return;
	
	params = params || {};
	
	var startPoint = params.startPoint || document;
		startPoint = ARA.dom.getElement(startPoint);
	
	var imgs = startPoint.getElementsByTagName("img");
	for(var i=0; i<imgs.length; i++){
		var classes = imgs[i].className.split(" ");
		for(var j=0; j<classes.length; j++){
			if(classes[j] == className){
				this.images.push({
					"element":imgs[i],
					"wrapImage":params.wrapImage || this.wrapImages,
					"alpha":params.alpha || this.alpha,
					"depth":params.depth || this.depth,
					"imageAsBlock":params.imageAsBlock || this.imageAsBlock
				});
			}
		}
	}
};


/**
 * Allows the passing of a previously collected array of images to be added onto
 * the images collection
 * @param  {Array}  array  An array of images
 * @param  {Object}  params  A JSON object of parameters:
 *                             {Boolean}  wrapImage     Optional. Whether or not to wrap an image and its reflection with a div
 *                             {Float}    alpha         Optional. Default: .25. Values from 0 to 1. The opacity to apply to the reflection
 *                             {Int}      depth         Optional. Default: half img height. How deep the reflection goes.
 *                             {Int}      imageAsBlock  Optional. Default: true. Whether or not to leave the image as display: inline or to set it as block (so that the reflection goes underneath it)
 */
ARA.effects.Reflection.prototype.addImagesRaw = function(array, params){
	if(!this.supportsCanvas) return;
	
	params = params || {};
	
	for(var i=0; i<array.length; i++){
		this.images.push({
			"element":array[i],
			"wrapImage":params.wrapImage || this.wrapImages,
			"alpha":params.alpha || this.alpha,
			"depth":params.depth || this.depth,
			"imageAsBlock":params.imageAsBlock || this.imageAsBlock
		});
	}
};


/**
 * Creates a canvas element, inserts it after the image and then fills it with
 * the image reflection
 * @param  {Object}  image  An image collection JSON object
 */
ARA.effects.Reflection.prototype.createReflection = function(image){
	if(!this.supportsCanvas) return;

	var img = image.element;
	if(image.imageAsBlock) img.style.display = "block";
	var depth = (image.depth == null) ? img.offsetHeight/2 : image.depth;
	depth=depth*(2/3);
	var alphaFactor = image.alpha;
	var cName = this.constants.canvasClassName;

	var canvas = document.createElement("canvas");
		canvas.id = img.id + this.constants.canvasID;
		canvas.className = (canvas.className.length > 0) ? canvas.className + cName : cName.substring(1, cName.length);
		canvas.width = img.offsetWidth;
		canvas.height = depth;
	var ctx = canvas.getContext("2d");
	for(var i=0; i<depth; i++){
		alpha = ((i!=0) ? (depth-i) / depth : depth) * alphaFactor;
		ctx.globalAlpha = alpha;
		ctx.drawImage(img, 0, img.offsetHeight - i, img.offsetWidth, 1, 0, i-10, img.offsetWidth, 1);
	}
	img.parentNode.insertBefore(canvas, img.nextSibling);
};

/**
 * Returns true or false depending on whether or not the browser supports Canvas
 * @returns {Boolean}
 */
ARA.effects.supportsCanvas = function(){
	var canvas = document.createElement("canvas");
	return (!!canvas.getContext);
};

ARA.dom = {};
ARA.dom.getElement = function(el){
	return (typeof el == "string") ? document.getElementById(el) : el;
};

ARA.dom.getElementsByClassName = function(className, startPoint){
	startPoint = ARA.dom.getElement(startPoint || document);
	var els = startPoint.getElementsByTagName("*");
	var elsByClass = [];
	
	for(var i=0; i<els.length; i++){
		var classes = els[i].className.split(" ");
		for(var j=0; j<classes.length; j++){
			if(classes[j] == className){
				elsByClass.push(els[i]);
			}
		}
	}
	return elsByClass;
};
